/*
*
* This is a simple Content Management System (CMS)
* Copyright (C) 2010 Imran M Yousuf (imyousuf@smartitengineering.com)
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.smartitengineering.cms.api.impl.content;
import com.smartitengineering.cms.api.content.BooleanFieldValue;
import com.smartitengineering.cms.api.content.CollectionFieldValue;
import com.smartitengineering.cms.api.content.CompositeFieldValue;
import com.smartitengineering.cms.api.content.Content;
import com.smartitengineering.cms.api.content.ContentFieldValue;
import com.smartitengineering.cms.api.content.ContentId;
import com.smartitengineering.cms.api.content.FieldValue;
import com.smartitengineering.cms.api.content.MutableCompositeFieldValue;
import com.smartitengineering.cms.api.content.MutableRepresentation;
import com.smartitengineering.cms.api.content.MutableVariation;
import com.smartitengineering.cms.api.content.Variation;
import com.smartitengineering.cms.api.factory.content.ContentLoader;
import com.smartitengineering.cms.api.content.DateTimeFieldValue;
import com.smartitengineering.cms.api.content.Field;
import com.smartitengineering.cms.api.content.Filter;
import com.smartitengineering.cms.api.content.MutableBooleanFieldValue;
import com.smartitengineering.cms.api.content.MutableCollectionFieldValue;
import com.smartitengineering.cms.api.content.MutableContentFieldValue;
import com.smartitengineering.cms.api.content.MutableDateTimeFieldValue;
import com.smartitengineering.cms.api.content.MutableField;
import com.smartitengineering.cms.api.content.MutableNumberFieldValue;
import com.smartitengineering.cms.api.content.MutableOtherFieldValue;
import com.smartitengineering.cms.api.content.MutableStringFieldValue;
import com.smartitengineering.cms.api.content.NumberFieldValue;
import com.smartitengineering.cms.api.content.OtherFieldValue;
import com.smartitengineering.cms.api.common.SearchResult;
import com.smartitengineering.cms.api.content.StringFieldValue;
import com.smartitengineering.cms.api.factory.content.WriteableContent;
import com.smartitengineering.cms.api.factory.write.Lock;
import com.smartitengineering.cms.api.impl.type.CompositionDataTypeImpl.EmbeddedContentDataTypeImpl;
import com.smartitengineering.cms.api.type.CollectionDataType;
import com.smartitengineering.cms.api.type.CompositeDataType;
import com.smartitengineering.cms.api.type.CompositeDataType.EmbeddedContentDataType;
import com.smartitengineering.cms.api.type.ContentDataType;
import com.smartitengineering.cms.api.type.ContentType;
import com.smartitengineering.cms.api.type.ContentTypeId;
import com.smartitengineering.cms.api.type.DataType;
import com.smartitengineering.cms.api.type.EnumDataType;
import com.smartitengineering.cms.api.type.FieldDef;
import com.smartitengineering.cms.api.type.FieldValueType;
import com.smartitengineering.cms.api.workspace.WorkspaceId;
import com.smartitengineering.cms.spi.SmartContentSPI;
import com.smartitengineering.cms.spi.content.PersistableContent;
import com.smartitengineering.dao.common.queryparam.QueryParameter;
import com.smartitengineering.dao.common.queryparam.QueryParameterFactory;
import java.io.ByteArrayInputStream;
import java.io.DataInputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Map;
import java.util.Map.Entry;
import java.util.UUID;
import org.apache.commons.codec.binary.Base64;
import org.apache.commons.codec.binary.StringUtils;
import org.apache.commons.codec.digest.DigestUtils;
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang.time.DateFormatUtils;
import org.codehaus.jackson.JsonNode;
import org.codehaus.jackson.node.ArrayNode;
import org.codehaus.jackson.node.ObjectNode;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
*
* @author kaisar
*/
public class ContentLoaderImpl implements ContentLoader {
protected final transient Logger logger = LoggerFactory.getLogger(getClass());
@Override
public MutableField createMutableField(ContentId contentId, FieldDef fieldDef) {
if (fieldDef != null) {
FieldImpl fieldImpl = new FieldImpl();
fieldImpl.setFieldDef(fieldDef);
fieldImpl.setName(fieldDef.getName());
fieldImpl.setContent(contentId);
return fieldImpl;
}
else {
throw new IllegalArgumentException("Argument can not be null.");
}
}
@Override
public MutableField createMutableField(Field field) {
if (field.getFieldDef() != null) {
FieldImpl fieldImpl = new FieldImpl();
fieldImpl.setName(field.getName());
fieldImpl.setValue(field.getValue());
fieldImpl.setFieldDef(field.getFieldDef());
return fieldImpl;
}
else {
throw new IllegalArgumentException("FieldDef can not be null.");
}
}
@Override
public MutableDateTimeFieldValue createDateTimeFieldValue(DateTimeFieldValue fieldValue) {
if (fieldValue.getValue() != null) {
DateTimeFieldValueImpl dateTimeFieldValueImpl = new DateTimeFieldValueImpl();
dateTimeFieldValueImpl.setValue(fieldValue.getValue());
return dateTimeFieldValueImpl;
}
else {
throw new IllegalArgumentException("Argument can not be null.");
}
}
@Override
public MutableBooleanFieldValue createBooleanFieldValue(BooleanFieldValue fieldValue) {
BooleanFieldValueImpl booleanFieldValueImpl = new BooleanFieldValueImpl();
booleanFieldValueImpl.setValue(fieldValue.getValue());
return booleanFieldValueImpl;
}
@Override
public MutableCollectionFieldValue createCollectionFieldValue(CollectionFieldValue fieldValue) {
if (fieldValue.getValue() != null) {
CollectionFieldValueImpl collectionFieldValueImpl = new CollectionFieldValueImpl();
collectionFieldValueImpl.setValue(fieldValue.getValue());
return collectionFieldValueImpl;
}
else {
throw new IllegalArgumentException("Argument can not be null.");
}
}
public MutableCompositeFieldValue createCompositeFieldValue(CompositeFieldValue fieldValue) {
if (fieldValue != null) {
CompositeFieldValueImpl valueImpl = new CompositeFieldValueImpl();
if (fieldValue.getValue() != null) {
valueImpl.setValue(fieldValue.getValue());
}
return valueImpl;
}
else {
throw new IllegalArgumentException("Argument can not be null.");
}
}
@Override
public MutableContentFieldValue createContentFieldValue(ContentFieldValue fieldValue) {
if (fieldValue.getValue() != null) {
ContentFieldValueImpl contentFieldValueImpl = new ContentFieldValueImpl();
contentFieldValueImpl.setValue(fieldValue.getValue());
return contentFieldValueImpl;
}
else {
throw new IllegalArgumentException("Argument can not be null.");
}
}
@Override
public MutableNumberFieldValue createNumberFieldValue(NumberFieldValue fieldValue) {
if (fieldValue.getValue() != null) {
NumberFieldValueImpl fieldValueImpl = new NumberFieldValueImpl();
fieldValueImpl.setValue(fieldValue.getValue());
fieldValueImpl.setFieldValueType(fieldValue.getDataType());
return fieldValueImpl;
}
else {
throw new IllegalArgumentException("Argument can not be null.");
}
}
@Override
public MutableOtherFieldValue createOtherFieldValue(OtherFieldValue fieldValue) {
if (fieldValue.getValue() != null) {
OtherFieldValueImpl otherFieldValueImpl = new OtherFieldValueImpl();
otherFieldValueImpl.setValue(fieldValue.getValue());
return otherFieldValueImpl;
}
else {
throw new IllegalArgumentException("Argument can not be null.");
}
}
@Override
public MutableStringFieldValue createStringFieldValue(StringFieldValue fieldValue) {
if (fieldValue.getValue() != null) {
StringFieldValueImpl stringFieldValueImpl = new StringFieldValueImpl();
stringFieldValueImpl.setValue(fieldValue.getValue());
return stringFieldValueImpl;
}
else {
throw new IllegalArgumentException("Argument can not be null.");
}
}
@Override
public ContentId createContentId(WorkspaceId workspaceId, byte[] id) {
if (workspaceId != null && id != null) {
ContentIdImpl contentIdImpl = new ContentIdImpl();
contentIdImpl.setId(id);
contentIdImpl.setWorkspaceId(workspaceId);
return contentIdImpl;
}
else {
throw new IllegalArgumentException("Argument can not be null.");
}
}
@Override
public Content loadContent(ContentId contentId) {
final Collection<Content> contents =
SmartContentSPI.getInstance().getContentReader().readContentsFromPersistentStorage(
contentId);
if (contents == null || contents.isEmpty()) {
return null;
}
return contents.iterator().next();
}
@Override
public Filter craeteFilter() {
return new FilterImpl();
}
@Override
public SearchResult<Content> search(Filter filter) {
return SmartContentSPI.getInstance().getContentSearcher().search(filter);
}
@Override
public String escapeStringForSearch(String string) {
return SmartContentSPI.getInstance().getContentSearcher().escapeStringForSearch(string);
}
@Override
public MutableDateTimeFieldValue createDateTimeFieldValue() {
final DateTimeFieldValueImpl dateTimeFieldValueImpl = new DateTimeFieldValueImpl();
dateTimeFieldValueImpl.setFieldValueType(FieldValueType.DATE_TIME);
return dateTimeFieldValueImpl;
}
@Override
public MutableBooleanFieldValue createBooleanFieldValue() {
final BooleanFieldValueImpl booleanFieldValueImpl = new BooleanFieldValueImpl();
booleanFieldValueImpl.setFieldValueType(FieldValueType.BOOLEAN);
return booleanFieldValueImpl;
}
@Override
public MutableCollectionFieldValue createCollectionFieldValue() {
final CollectionFieldValueImpl collectionFieldValueImpl = new CollectionFieldValueImpl();
collectionFieldValueImpl.setFieldValueType(FieldValueType.COLLECTION);
return collectionFieldValueImpl;
}
public MutableCompositeFieldValue createCompositeFieldValue() {
return new CompositeFieldValueImpl();
}
@Override
public MutableContentFieldValue createContentFieldValue() {
final ContentFieldValueImpl contentFieldValueImpl = new ContentFieldValueImpl();
contentFieldValueImpl.setFieldValueType(FieldValueType.CONTENT);
return contentFieldValueImpl;
}
@Override
public MutableNumberFieldValue createDoubleFieldValue() {
final NumberFieldValueImpl numberFieldValueImpl = new NumberFieldValueImpl();
numberFieldValueImpl.setFieldValueType(FieldValueType.DOUBLE);
return numberFieldValueImpl;
}
@Override
public MutableNumberFieldValue createIntegerFieldValue() {
final NumberFieldValueImpl numberFieldValueImpl = new NumberFieldValueImpl();
numberFieldValueImpl.setFieldValueType(FieldValueType.INTEGER);
return numberFieldValueImpl;
}
@Override
public MutableNumberFieldValue createLongFieldValue() {
final NumberFieldValueImpl numberFieldValueImpl = new NumberFieldValueImpl();
numberFieldValueImpl.setFieldValueType(FieldValueType.LONG);
return numberFieldValueImpl;
}
@Override
public MutableOtherFieldValue createOtherFieldValue() {
final OtherFieldValueImpl otherFieldValueImpl = new OtherFieldValueImpl();
otherFieldValueImpl.setFieldValueType(FieldValueType.OTHER);
return otherFieldValueImpl;
}
@Override
public MutableStringFieldValue createStringFieldValue() {
final StringFieldValueImpl stringFieldValueImpl = new StringFieldValueImpl();
stringFieldValueImpl.setFieldValueType(FieldValueType.STRING);
return stringFieldValueImpl;
}
@Override
public Variation getVariation(Content content, Field field, String name) {
return SmartContentSPI.getInstance().getVariationProvider().getVariation(name, content, field);
}
@Override
public WriteableContent createContent(ContentType contentType) {
return createContent(contentType, false);
}
@Override
public WriteableContent createContent(ContentType contentType, boolean supressChecking) {
PersistableContent content = SmartContentSPI.getInstance().getPersistableDomainFactory().createPersistableContent(
supressChecking);
content.setContentDefinition(contentType);
return content;
}
@Override
public WriteableContent getWritableContent(Content content) {
if (content instanceof WriteableContent) {
return (WriteableContent) content;
}
else {
return getWritableContent(content, false);
}
}
@Override
public WriteableContent getWritableContent(Content content, boolean supressChecking) {
return getWritableContent(content, null, supressChecking);
}
public WriteableContent getWritableContent(Content content, Lock encapsulatingLock) {
if (content instanceof PersistableContent) {
final PersistableContent mutableContent = (PersistableContent) content;
if (encapsulatingLock != null) {
mutableContent.setLock(encapsulatingLock);
}
return mutableContent;
}
else {
return getWritableContent(content, encapsulatingLock, false);
}
}
public WriteableContent getWritableContent(Content content, Lock encapsulatingLock, boolean supressChecking) {
final PersistableContent mutableContent;
mutableContent = SmartContentSPI.getInstance().getPersistableDomainFactory().
createPersistableContent(supressChecking);
mutableContent.setContentDefinition(content.getContentDefinition());
mutableContent.setContentId(content.getContentId());
mutableContent.setCreationDate(content.getCreationDate());
mutableContent.setLastModifiedDate(content.getLastModifiedDate());
mutableContent.setParentId(content.getParentId());
mutableContent.setStatus(content.getStatus());
for (Field field : content.getFields().values()) {
mutableContent.setField(field);
}
if (encapsulatingLock != null) {
mutableContent.setLock(encapsulatingLock);
}
return mutableContent;
}
@Override
public FieldValue getValueFor(String value, DataType dataType) {
final FieldValue result;
final FieldValueType type = dataType.getType();
if (logger.isDebugEnabled()) {
logger.debug("Getting field value as " + type.name() + " from " + value);
}
switch (type) {
case COMPOSITE:
logger.debug("Getting as composite");
MutableCompositeFieldValue compositeFieldValue = createCompositeFieldValue();
try {
JsonNode node = CollectionFieldValueImpl.MAPPER.readTree(value);
if (node instanceof ObjectNode) {
ObjectNode objectNode = (ObjectNode) node;
CompositeDataType compositeDataType = (CompositeDataType) dataType;
Map<String, FieldDef> defs = compositeDataType.getComposedFieldDefs();
Collection<Field> compositeValues = new ArrayList<Field>(defs.size());
for (Entry<String, FieldDef> def : defs.entrySet()) {
final JsonNode fNode = objectNode.get(def.getKey());
final DataType itemDataType = def.getValue().getValueDef();
final String textValue;
if (fNode != null) {
if (itemDataType.getType().equals(FieldValueType.COLLECTION) || itemDataType.getType().equals(
FieldValueType.COMPOSITE)) {
textValue = fNode.toString();
}
else {
textValue = fNode.getTextValue();
}
}
else {
textValue = "";
}
final FieldValue valueFor = org.apache.commons.lang.StringUtils.isNotBlank(textValue) ? getValueFor(
textValue, itemDataType) : null;
final MutableField mutableField = createMutableField(null, def.getValue());
mutableField.setValue(valueFor);
compositeValues.add(mutableField);
}
compositeFieldValue.setValue(compositeValues);
}
else {
throw new IllegalStateException("Collection must be of array of strings!");
}
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
result = compositeFieldValue;
break;
case COLLECTION:
logger.debug("Getting as collection");
MutableCollectionFieldValue collectionFieldValue = createCollectionFieldValue();
try {
JsonNode node = CollectionFieldValueImpl.MAPPER.readTree(value);
if (node instanceof ArrayNode) {
ArrayNode arrayNode = (ArrayNode) node;
int size = arrayNode.size();
ArrayList<FieldValue> values = new ArrayList<FieldValue>(size);
for (int i = 0; i < size; ++i) {
final DataType itemDataType = ((CollectionDataType) dataType).getItemDataType();
if (itemDataType.getType().equals(FieldValueType.COMPOSITE)) {
final String stringValue = arrayNode.get(i).toString();
values.add(getValueFor(stringValue, itemDataType));
}
else {
final String stringValue = arrayNode.get(i).getTextValue();
values.add(getValueFor(stringValue, itemDataType));
}
}
collectionFieldValue.setValue(values);
}
else {
throw new IllegalStateException("Collection must be of array of strings!");
}
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
result = collectionFieldValue;
break;
default:
logger.debug("Getting as all other than collection");
result = getSimpleValueFor(value, type);
}
return result;
}
public FieldValue getSimpleValueFor(String value, FieldValueType type) {
final FieldValue result;
switch (type) {
case BOOLEAN:
logger.debug("Getting as boolean");
MutableBooleanFieldValue booleanFieldValue = createBooleanFieldValue();
result = booleanFieldValue;
booleanFieldValue.setValue(Boolean.parseBoolean(value));
break;
case CONTENT:
logger.debug("Getting as content");
MutableContentFieldValue contentFieldValue = createContentFieldValue();
if (logger.isDebugEnabled()) {
logger.debug("Content value: " + value);
}
try {
DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(StringUtils.getBytesUtf8(value)));
ContentIdImpl idImpl = new ContentIdImpl();
idImpl.readExternal(inputStream);
contentFieldValue.setValue(idImpl);
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
result = contentFieldValue;
break;
case INTEGER:
logger.debug("Getting as integer");
MutableNumberFieldValue integerFieldValue = createIntegerFieldValue();
integerFieldValue.setValue(NumberUtils.toInt(value, Integer.MIN_VALUE));
result = integerFieldValue;
break;
case DOUBLE:
logger.debug("Getting as double");
MutableNumberFieldValue doubleFieldValue = createDoubleFieldValue();
doubleFieldValue.setValue(NumberUtils.toDouble(value, Double.MIN_VALUE));
result = doubleFieldValue;
break;
case LONG:
logger.debug("Getting as long");
MutableNumberFieldValue longFieldValue = createLongFieldValue();
longFieldValue.setValue(NumberUtils.toLong(value, Long.MIN_VALUE));
result = longFieldValue;
break;
case DATE_TIME:
logger.debug("Getting as date time");
MutableDateTimeFieldValue valueOf;
try {
valueOf = DateTimeFieldValueImpl.valueOf(value);
}
catch (Exception ex) {
throw new RuntimeException(ex);
}
result = valueOf;
break;
case OTHER:
logger.debug("Getting as other");
MutableOtherFieldValue otherFieldValue = createOtherFieldValue();
otherFieldValue.setValue(Base64.decodeBase64(value));
result = otherFieldValue;
break;
case STRING:
default:
logger.debug("Getting as else or string");
MutableStringFieldValue fieldValue = createStringFieldValue();
fieldValue.setValue(value);
result = fieldValue;
}
return result;
}
@Override
public ContentId generateContentId(WorkspaceId workspaceId) {
UUID uid = UUID.randomUUID();
return createContentId(workspaceId, StringUtils.getBytesUtf8(uid.toString()));
}
@Override
public String getEntityTagValueForContent(Content content) {
return DigestUtils.md5Hex(new StringBuilder(DateFormatUtils.ISO_DATETIME_TIME_ZONE_FORMAT.format(content.
getLastModifiedDate())).append('~').append(content.getOwnFields().toString()).append('~').append(content.
getStatus()).toString());
}
@Override
public MutableRepresentation createMutableRepresentation(ContentId contentId) {
return new RepresentationImpl(contentId);
}
@Override
public MutableVariation createMutableVariation(ContentId contentId, FieldDef fieldDef) {
return new VariationImpl(contentId, fieldDef);
}
@Override
public boolean isValidContent(Content content) {
if (!isValid(content)) {
return false;
}
if (!checkForImpliedValidations(content)) {
return false;
}
if (!isValidByCustomValidators(content)) {
return false;
}
return true;
}
protected boolean isValid(Content content) {
if (logger.isDebugEnabled()) {
logger.debug("Content: " + content);
if (content != null) {
logger.debug("Content ID: " + content.getContentId());
logger.debug("Content Definition: " + content.getContentDefinition());
if (content.getContentDefinition() != null) {
logger.debug("Required fields present: " + isMandatoryFieldsPresent(content));
}
}
}
if (logger.isWarnEnabled() && !(content != null && content.getContentId() != null && content.getContentDefinition() !=
null)) {
logger.warn("Content or its ID or content definition is missing!");
}
return content != null && content.getContentId() != null && content.getContentDefinition() != null &&
content.getContentDefinition().getDefinitionType().equals(ContentType.DefinitionType.CONCRETE_TYPE) &&
isMandatoryFieldsPresent(content);
}
protected boolean isMandatoryFieldsPresent(Content content) {
ContentType type = content.getContentDefinition();
return isMandatoryFieldsPresent(type.getFieldDefs().values(), content.getFields());
}
protected boolean isMandatoryFieldsPresent(Collection<FieldDef> defs, Map<String, Field> fields) {
boolean valid = true;
for (FieldDef def : defs) {
if (!valid) {
logger.debug("Skipping field as already invalid! - " + def.getName());
continue;
}
final Field cField = fields.get(def.getName());
if (logger.isDebugEnabled()) {
logger.debug(def.getName() + " is required: " + def.isRequired());
logger.debug(def.getName() + ": " + cField);
if (cField != null) {
logger.debug(def.getName() + ": " + cField.getValue());
if (cField.getValue() != null) {
logger.debug(def.getName() + ": " + cField.getValue().getValue());
}
}
}
if (def.isRequired() && (cField == null || cField.getValue() == null || cField.getValue().getValue() == null)) {
if (logger.isWarnEnabled()) {
logger.warn("Required field not present " + def.getName());
}
valid = false;
}
if (valid) {
if (def.getValueDef().getType().equals(FieldValueType.COMPOSITE)) {
final Collection<FieldDef> nestedDefs;
final Map<String, Field> nestedFields;
nestedDefs = ((CompositeDataType) def.getValueDef()).getComposition();
if (fields.containsKey(def.getName()) && cField != null) {
Field myField = cField;
CompositeFieldValue compositeFieldValue = (CompositeFieldValue) myField.getValue();
if (compositeFieldValue != null) {
nestedFields = compositeFieldValue.getValueAsMap();
}
else {
nestedFields = null;
}
}
else {
nestedFields = null;
}
if (nestedDefs != null && nestedFields != null) {
if (logger.isDebugEnabled()) {
logger.debug("Going to validate collection's item mandatory fields from " + cField.getName());
}
valid = isMandatoryFieldsPresent(nestedDefs, nestedFields);
}
}
else if (def.getValueDef().getType().equals(FieldValueType.COLLECTION) &&
((CollectionDataType) def.getValueDef()).getItemDataType().getType().equals(FieldValueType.COMPOSITE)) {
final Collection<FieldDef> nestedDefs;
nestedDefs = ((CompositeDataType) ((CollectionDataType) def.getValueDef()).getItemDataType()).getComposition();
if (fields.containsKey(def.getName()) && cField != null) {
final Field myField = cField;
CollectionFieldValue collectionFieldValue = (CollectionFieldValue) myField.getValue();
if (collectionFieldValue != null) {
Collection<FieldValue> values = collectionFieldValue.getValue();
if (values != null) {
for (FieldValue fieldValue : values) {
if (!valid) {
continue;
}
CompositeFieldValue compositeFieldValue = (CompositeFieldValue) fieldValue;
final Map<String, Field> nestedFields;
if (compositeFieldValue != null) {
nestedFields = compositeFieldValue.getValueAsMap();
}
else {
nestedFields = null;
}
if (nestedDefs != null && nestedFields != null) {
if (logger.isDebugEnabled()) {
logger.debug("Going to validate collection's item mandatory fields from " + myField.getName());
}
valid = valid && isMandatoryFieldsPresent(nestedDefs, nestedFields);
}
}
}
}
}
}
}
}
return valid;
}
protected boolean isValidByCustomValidators(Content content) {
final Collection<Field> values = content.getFields().values();
return isValidByCustomValidators(values, content);
}
protected boolean isValidByCustomValidators(final Collection<Field> values, final Content content) {
boolean valid = true;
for (Field field : values) {
if (!valid) {
if (logger.isDebugEnabled()) {
logger.debug("Skipping field as already invalid! - " + field.getName());
}
continue;
}
if (field == null || field.getFieldDef() == null || field.getValue() == null) {
if (field != null && logger.isWarnEnabled()) {
if (field.getFieldDef() != null) {
logger.warn("The field def is null for " + field.getName());
}
else if (field.getValue() != null) {
logger.warn("The field value is null for " + field.getName());
}
}
else {
logger.warn("Field is NULL");
}
continue;
}
if (logger.isDebugEnabled()) {
logger.debug("Attempting custom validation for " + field.getName());
}
FieldDef def = field.getFieldDef();
if (def.getValueDef().getType().equals(FieldValueType.COMPOSITE)) {
final Map<String, Field> nestedFields;
CompositeFieldValue compositeFieldValue = (CompositeFieldValue) field.getValue();
if (compositeFieldValue != null) {
nestedFields = compositeFieldValue.getValueAsMap();
}
else {
nestedFields = null;
}
if (nestedFields != null) {
if (logger.isDebugEnabled()) {
logger.debug("Going to validate composite fields from " + field.getName());
}
valid = isValidByCustomValidators(nestedFields.values(), content);
}
}
else if (def.getValueDef().getType().equals(FieldValueType.COLLECTION) &&
((CollectionDataType) def.getValueDef()).getItemDataType().getType().equals(FieldValueType.COMPOSITE)) {
CollectionFieldValue collectionFieldValue = (CollectionFieldValue) field.getValue();
Collection<FieldValue> collectionValues = collectionFieldValue.getValue();
if (collectionValues != null) {
for (FieldValue fieldValue : collectionValues) {
if (!valid) {
logger.debug("Skipping iteration over collection field as already invalid! - " + field.getName());
continue;
}
CompositeFieldValue compositeFieldValue = (CompositeFieldValue) fieldValue;
final Map<String, Field> nestedFields;
if (compositeFieldValue != null) {
nestedFields = compositeFieldValue.getValueAsMap();
}
else {
nestedFields = null;
}
if (nestedFields != null) {
if (logger.isDebugEnabled()) {
logger.debug("Going to validate colletion's item composite fields from " + field.getName());
}
valid = isValidByCustomValidators(nestedFields.values(), content);
}
}
}
}
if (valid) {
//Check current field validation after the nested fields are checked
final boolean validField = SmartContentSPI.getInstance().getValidatorProvider().isValidField(content, field);
if (!validField && logger.isWarnEnabled()) {
logger.warn("Custom field validation failed for " + field.getName());
}
valid = validField;
}
}
return valid;
}
protected void addQueryForContentId(final ContentId contentId, ContentTypeId instanceOfId, Filter filter) {
QueryParameter<String> param = QueryParameterFactory.getStringLikePropertyParam("id",
new StringBuilder().append(contentId.
toString()).toString());
filter.addFieldFilter(param);
filter.addContentTypeToFilter(instanceOfId);
}
protected boolean checkForImpliedValidations(Collection<Field> fields) {
boolean valid = true;
for (Field field : fields) {
if (field != null && field.getFieldDef() != null && field.getValue() != null) {
FieldDef def = field.getFieldDef();
switch (def.getValueDef().getType()) {
case COLLECTION:
final CollectionDataType collDataType = (CollectionDataType) def.getValueDef();
if (collDataType.getItemDataType().getType().equals(FieldValueType.CONTENT)) {
DataType contentDataType = collDataType.getItemDataType();
for (FieldValue val : ((CollectionFieldValue) field.getValue()).getValue()) {
final boolean checkContentTypeValidity = checkContentTypeValidity(val, contentDataType);
if (!checkContentTypeValidity && logger.isWarnEnabled()) {
logger.warn("Content relation failed in " + field.getName());
}
valid = valid && checkContentTypeValidity;
}
}
else if (collDataType.getItemDataType().getType().equals(FieldValueType.COMPOSITE)) {
for (FieldValue val : ((CollectionFieldValue) field.getValue()).getValue()) {
final boolean checkForEnumValidity = checkForImpliedValidations(((CompositeFieldValue) val).getFields().
values());
if (!checkForEnumValidity && logger.isWarnEnabled()) {
logger.warn("Composite relation check in collection failed for " + field.getName());
}
valid = valid && checkForEnumValidity;
}
}
else if (collDataType.getItemDataType().getType().equals(FieldValueType.ENUM)) {
EnumDataType enumDataType = (EnumDataType) collDataType.getItemDataType();
Collection<String> choices = enumDataType.getChoices();
for (FieldValue val : ((CollectionFieldValue) field.getValue()).getValue()) {
if (logger.isDebugEnabled()) {
logger.debug("Enum field value instance of string field value " + (val instanceof StringFieldValue));
}
if (val instanceof StringFieldValue) {
String strval = ((StringFieldValue) val).getValue();
final boolean checkForEnumValidity = choices.contains(strval);
if (!checkForEnumValidity && logger.isWarnEnabled()) {
logger.warn("Enum value in collection failed for " + field.getName());
}
valid = valid && checkForEnumValidity;
}
}
}
break;
case CONTENT: {
final boolean checkContentTypeValidity = checkContentTypeValidity(field.getValue(), def.getValueDef());
if (!checkContentTypeValidity && logger.isWarnEnabled()) {
logger.warn("Content relation failed in " + field.getName());
}
valid = valid && checkContentTypeValidity;
}
break;
case COMPOSITE: {
final boolean checkForEnumValidity = checkForImpliedValidations(((CompositeFieldValue) field.getValue()).
getFields().values());
if (!checkForEnumValidity && logger.isWarnEnabled()) {
logger.warn("Composite relation check in collection failed for " + field.getName());
}
valid = valid && checkForEnumValidity;
}
case ENUM: {
if (field.getValue() instanceof StringFieldValue) {
Collection<String> choices = ((EnumDataType) def.getValueDef()).getChoices();
String strval = ((StringFieldValue) field.getValue()).getValue();
final boolean checkForEnumValidity = choices.contains(strval);
if (!checkForEnumValidity && logger.isWarnEnabled()) {
logger.warn("Enum value failed for " + field.getName());
}
valid = valid && checkForEnumValidity;
}
}
break;
default:
break;
}
}
if (!valid) {
return valid;
}
}
return valid;
}
protected boolean checkForImpliedValidations(Content content) {
final Collection<Field> values = content.getFields().values();
return checkForImpliedValidations(values);
}
protected boolean checkContentTypeValidity(FieldValue val, DataType contentDataType) {
Filter filter = craeteFilter();
addQueryForContentId(((ContentFieldValue) val).getValue(), ((ContentDataType) contentDataType).getTypeDef(), filter);
filter.setDisjunction(false);
Collection<Content> contents = search(filter).getResult();
return !contents.isEmpty();
}
@Override
public SearchResult<Content> createSearchResult(Collection<Content> result, long totalResultsCount) {
SearchResultImpl<Content> resultImpl = new SearchResultImpl<Content>();
resultImpl.setResult(result);
resultImpl.setTotalResultsCount(totalResultsCount);
return resultImpl;
}
@Override
public void reIndex(ContentId contentId) {
SmartContentSPI.getInstance().getContentSearcher().reIndex(contentId);
}
@Override
public void reIndex(WorkspaceId workspaceId) {
SmartContentSPI.getInstance().getContentSearcher().reIndex(workspaceId);
}
@Override
public ContentId parseContentId(String contentIdStr) {
byte[] idBytes = org.apache.commons.codec.binary.StringUtils.getBytesUtf8(contentIdStr);
DataInputStream inputStream = new DataInputStream(new ByteArrayInputStream(idBytes));
ContentIdImpl idImpl = new ContentIdImpl();
try {
idImpl.readExternal(inputStream);
}
catch (Exception ex) {
logger.warn("Could not parse content id string " + contentIdStr, ex);
return null;
}
return idImpl;
}
public EmbeddedContentDataType createEmbeddedContentDataType(FieldDef def, ContentDataType type) {
return new EmbeddedContentDataTypeImpl(def, type);
}
}